/*
 * Quadruples.cpp
 *
 *  Created on: Nov 25, 2011
 *      Author: bkoziak
 */

#include "Quadruples.h"
#include "parser.h"
#include <iostream>

Quadruples* Quadruples::instance = NULL;

Quadruples* Quadruples::getInstance() {
	if (instance == NULL) {
		instance = new Quadruples();
	}
	return instance;
}

void Quadruples::skok(Quadruple* q) {
	quadrupleVector.push_back(q);
}
Symbol* Quadruples::wstaw(Quadruple* q) {
	Symbol* rezultat = NULL;
	if (q->getTypOperatora() == LABEL) {
	}

	if (q->getTypOperatora() == ASSIGNOP || q->getTypOperatora() == MOV) {
		rezultat = q->getArg2();
	}
	quadrupleVector.push_back(q);
	return rezultat;
}

void Quadruples::przypisz(Symbol* s1, Symbol* s2) {
	if (s1->getTypSymbolu() != Symbol::ZMIENNA) {
		//TODO:Obsluga bledu
	}
	//optymalizacja, czy miejsce do rzucenia bledu??
	if (s1 == s2) {
		return;
	}
	//TODO: WHY? if(argument->getType() == (int)SymbolEntry::CONST) return argument;
	Symbol* tmp = NULL;
	if (s2->getTypSymbolu() == Symbol::STALA)
		tmp = s2;
	if (s1->getTypZmiennej() != s2->getTypZmiennej() && tmp == NULL) {
		if (s2->getTypZmiennej() == Symbol::INTEGER) {
			tmp = intToReal(s2);
		} else {
			tmp = realToInt(s2);
		}
	} else {
		tmp = s2;
	}
	Quadruple* q = new Quadruple(ASSIGNOP, tmp, s1);
	wstaw(q);

}

string Quadruples::stworzLabelDlaWhile() {
	string label = getNastepnyLabel();
	labeleDlaWhile.push_back(StringVector::value_type(label));
	return label;
}

string Quadruples::getLabelDlaWhile() {
	string label = labeleDlaWhile.at(labeleDlaWhile.size() - 1);
	labeleDlaWhile.pop_back();
	return label;
}

string Quadruples::stworzLabelDlaIf() {
	string label = getNastepnyLabel();
	labeleDlaIf.push_back(StringVector::value_type(label));
	return label;
}

string Quadruples::getLabelDlaIf() {
	string label = labeleDlaIf.at(labeleDlaIf.size() - 1);
	labeleDlaIf.pop_back();
	return label;
}

string Quadruples::stworzLabelDlaThen() {
	string label = getNastepnyLabel();
	labeleDlaThen.push_back(StringVector::value_type(label));
	return label;
}

string Quadruples::getLabelDlaThen() {
	string label = labeleDlaThen.at(labeleDlaThen.size() - 1);
	labeleDlaThen.pop_back();
	return label;
}

Symbol* Quadruples::dodaj(int znak, Symbol* s1, Symbol* s2) {
	if (s1->getTypZmiennej() != s2->getTypZmiennej()) {
		if (s1->getTypSymbolu() != Symbol::STALA
				&& s1->getTypZmiennej() == Symbol::INTEGER) {
			s1 = intToReal(s1);
		} else if (s2->getTypSymbolu() != Symbol::STALA
				&& s2->getTypZmiennej() == Symbol::INTEGER) {
			s2 = realToInt(s2);
		}
	}
	Quadruple* q = new Quadruple(znak, s1, s2);
	TablicaSymboli* tablica = TablicaSymboli::getInstance();
	Symbol* wynik = tablica->stworzZmiennaTymczasowa(q->getTypZmiennej());
	q->setRezultat(wynik);
	quadrupleVector.push_back(q);
	return wynik;

}

Symbol* Quadruples::pomnoz(int znak, Symbol* s1, Symbol* s2) {
	if (s1->getTypZmiennej() != s2->getTypZmiennej()) {
		if (s1->getTypSymbolu() != Symbol::STALA
				&& s1->getTypZmiennej() == Symbol::INTEGER) {
			s1 = intToReal(s1);
		} else if (s2->getTypSymbolu() != Symbol::STALA
				&& s2->getTypZmiennej() == Symbol::INTEGER) {
			s2 = realToInt(s2);
		}
	}
	Quadruple* q = new Quadruple(znak, s1, s2);
	TablicaSymboli* tablica = TablicaSymboli::getInstance();
	Symbol* wynik = tablica->stworzZmiennaTymczasowa(q->getTypZmiennej());
	q->setRezultat(wynik);
	quadrupleVector.push_back(q);
	return wynik;
}
Symbol* Quadruples::porownaj(int op, Symbol* s1, Symbol* s2) {
	if (s1->getTypZmiennej() != s2->getTypZmiennej()) {
		if (s1->getTypSymbolu() != Symbol::STALA
				&& s1->getTypZmiennej() == Symbol::INTEGER) {
			s1 = intToReal(s1);
		} else if (s2->getTypSymbolu() != Symbol::STALA
				&& s2->getTypZmiennej() == Symbol::INTEGER) {
			s2 = realToInt(s2);
		}
	}
	TablicaSymboli* tablica = TablicaSymboli::getInstance();

	Symbol* tmp = tablica->stworzZmiennaTymczasowa(Symbol::INTEGER);
	Symbol* jeden = tablica->insert("1", NUM);
	Symbol* zero = tablica->insert("0", NUM);
	string labelka = getNastepnyLabel();
	Quadruple* etykieta = new Quadruple(LABEL, 0, 0);
	etykieta->setLabel(labelka);
	switch (op) {
	case EQ:
		wstaw(new Quadruple(MOV, jeden, tmp));
		skok(new Quadruple(JEQ, s1, s2, labelka));
		wstaw(new Quadruple(MOV, zero, tmp));
		wstaw(etykieta);
		break;
	}
	return tmp;
}

Symbol* Quadruples::realToInt(Symbol* arg) {
	TablicaSymboli* tablica = TablicaSymboli::getInstance();
	Symbol* rezultat = tablica->stworzZmiennaTymczasowa(Symbol::INTEGER);
	Quadruple* q = new Quadruple(REALTOINT, arg, NULL, rezultat);
	quadrupleVector.push_back(q);
	return rezultat;
}

Symbol* Quadruples::intToReal(Symbol* arg) {
	TablicaSymboli* tablica = TablicaSymboli::getInstance();
	Symbol* rezultat = tablica->stworzZmiennaTymczasowa(Symbol::INTEGER);
	Quadruple* q = new Quadruple(INTTOREAL, arg, NULL, rezultat);
	quadrupleVector.push_back(q);
	return rezultat;
}

string Quadruples::getAsm() {
	stringstream ss;
	for (unsigned int i = 0; i < quadrupleVector.size(); ++i) {
		ss << quadrupleVector.at(i)->getAsm();
	}
	return ss.str();
}
string Quadruples::getNastepnyLabel() {
	stringstream ss;
	ss << "label" << numerAktualnegoLabela++;
	return ss.str();
}
Quadruples::Quadruples() {
	numerAktualnegoLabela = 0;
}

Symbol* Quadruples::wywolaj(Symbol* funkcja, WektorSymboli parametry,
		WektorSymboli argumenty) {
	int rozmiarZmiennych = 0;
	if (wywolajJesliWbudowana(funkcja, argumenty) == NULL) {
		return NULL;
	}
	for (unsigned int i = 0; i < argumenty.size(); ++i) {
		Symbol* parametr = ustawArgument(parametry.at(i), argumenty.at(i));
		Quadruple* q = new Quadruple(PUSH, parametr, NULL);
		wstaw(q);
		rozmiarZmiennych += 4;
	}
	Symbol* rezultat = NULL;
	if (funkcja->getTypSymbolu() == Symbol::FUNCKJA) {
		TablicaSymboli* tablica = TablicaSymboli::getInstance();
		rezultat = tablica->stworzZmiennaTymczasowa(funkcja->getTypZmiennej());
		rezultat->setTypZmiennej(funkcja->getTypZmiennej());
		Quadruple* q1 = new Quadruple(PUSH, rezultat, NULL);
		wstaw(q1);
		rozmiarZmiennych += 4;
	}
	Quadruple* call = new Quadruple(CALL, funkcja, NULL);
	Quadruple* incsp = new Quadruple(INCSP, funkcja, NULL);
	wstaw(call);
	wstaw(incsp);
	return rezultat;
}
Symbol* Quadruples::wywolajJesliWbudowana(Symbol* funkcja,
		WektorSymboli argumenty) {
	if (funkcja->getLexem() == "write") {
		for (unsigned int i = 0; i < argumenty.size(); ++i) {
			Quadruple* write = new Quadruple(WRITE, argumenty.at(i), NULL);
			wstaw(write);
		}
		return NULL;
	}
	if (funkcja->getLexem() == "read") {
		for (unsigned int i = 0; i < argumenty.size(); ++i) {
			Quadruple* read = new Quadruple(READ, argumenty.at(i), NULL);
			wstaw(read);
		}
		return NULL;
	}
	return funkcja;
}

void Quadruples::rozpocznijFunkcje(Symbol* funkcja) {
	Quadruple* enter = new Quadruple(ENTER, funkcja, NULL);
	wstaw(enter);
}
void Quadruples::zakonczFunkcje() {
	Quadruple* leave = new Quadruple(LEAVE, NULL, NULL);
	wstaw(leave);
}
Symbol* Quadruples::ustawArgument(Symbol* parametr, Symbol* argument) {
	if (argument->getTypSymbolu() == Symbol::STALA) {
		return stalaDoZmiennej(argument, parametr->getTypZmiennej());
	}
	return dopasujTypy(parametr, argument);
}
Symbol* Quadruples::stalaDoZmiennej(Symbol* stala,
		Symbol::TYP_ZMIENNEJ typDocelowy) {
	TablicaSymboli* tablica = TablicaSymboli::getInstance();
	if (typDocelowy == Symbol::INTEGER
			&& typDocelowy != stala->getTypZmiennej()) {
		tablica->insert(stala->zamienNaLexem((int) stala->getStalaWartosc()),
				NUM);
	}
	Symbol* zmienna = tablica->stworzZmiennaTymczasowa(typDocelowy);
	Quadruple* q = new Quadruple(MOV, stala, zmienna);
	return wstaw(q);
}

Symbol* Quadruples::dopasujTypy(Symbol* parametr, Symbol* argument) {
	if (argument->getTypSymbolu() == Symbol::STALA) {
		return argument;
	}
	if (argument->getTypZmiennej() != parametr->getTypZmiennej()) {
		if (argument->getTypZmiennej() == Symbol::INTEGER) {
			return intToReal(argument);
		} else {
			return realToInt(argument);
		}
	} else {
		return argument;
	}

}
Quadruples::~Quadruples() {
	// TODO Auto-generated destructor stub
}

